package com.like;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;

/**
 * Created by Miroslaw Stanek on 21.12.2015.
 * Modified by Joel Dean
 */
public class DotsView extends Component implements Component.DrawTask, Component.EstimateSizeListener,
        Component.LayoutRefreshedListener {
    private static final int DOTS_COUNT = 7;
    private static final int OUTER_DOTS_POSITION_ANGLE = 51;

    private int COLOR_1 = 0xFFFFC107;
    private int COLOR_2 = 0xFFFF9800;
    private int COLOR_3 = 0xFFFF5722;
    private int COLOR_4 = 0xFFF44336;

    private int width = 0;
    private int height = 0;

    private final Paint[] circlePaints = new Paint[4];

    private int centerX;
    private int centerY;

    private float maxOuterDotsRadius;
    private float maxInnerDotsRadius;
    private float maxDotSize;

    private float currentProgress = 0;

    private float currentRadius1 = 0;
    private float currentDotSize1 = 0;

    private float currentDotSize2 = 0;
    private float currentRadius2 = 0;

    private ArgbEvaluator argbEvaluator = new ArgbEvaluator();

    public DotsView(Context context) {
        super(context);
        init();
    }

    public DotsView(Context context, AttrSet attrs) {
        super(context, attrs);
        init();
    }

    public DotsView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setEstimateSizeListener(this);
        setLayoutRefreshedListener(this);

        for (int i = 0; i < circlePaints.length; i++) {
            circlePaints[i] = new Paint();
            circlePaints[i].setStyle(Paint.Style.FILL_STYLE);
            circlePaints[i].setAntiAlias(true);
        }
        onRefreshed(this);
        addDrawTask(this);
        invalidate();
    }

    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        if (width != 0 && height != 0) {
            setEstimatedSize(width, height);
        }
        return false;
    }

    @Override
    public void onRefreshed(Component component) {
        centerX = component.getWidth() / 2;
        centerY = component.getHeight() / 2;
        maxDotSize = 6;
        maxOuterDotsRadius = component.getWidth() / 2 - maxDotSize * 2;
        maxInnerDotsRadius = (80 * maxOuterDotsRadius) / 100;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        drawOuterDotsFrame(canvas);
        drawInnerDotsFrame(canvas);
    }

    private void drawOuterDotsFrame(Canvas canvas) {
        for (int i = 0; i < DOTS_COUNT; i++) {
            int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
            int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
            canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);
        }
    }

    private void drawInnerDotsFrame(Canvas canvas) {
        for (int i = 0; i < DOTS_COUNT; i++) {
            int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
            int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
            canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);
        }
    }

    public void setCurrentProgress(float currentProgress) {
        this.currentProgress = currentProgress;

        updateInnerDotsPosition();
        updateOuterDotsPosition();
        updateDotsPaints();
        updateDotsAlpha();
        invalidate();
    }

    public float getCurrentProgress() {
        return currentProgress;
    }

    private void updateInnerDotsPosition() {
        if (currentProgress < 0.3f) {
            this.currentRadius2 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0, 0.3f, 0.f, maxInnerDotsRadius);
        } else {
            this.currentRadius2 = maxInnerDotsRadius;
        }
        if (currentProgress == 0) {
            this.currentDotSize2 = 0;
        } else if (currentProgress < 0.2) {
            this.currentDotSize2 = maxDotSize;
        } else if (currentProgress < 0.5) {
            this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.2f, 0.5f, maxDotSize, 0.3 * maxDotSize);
        } else {
            this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.5f, 1f, maxDotSize * 0.3f, 0);
        }
    }

    private void updateOuterDotsPosition() {
        if (currentProgress < 0.3f) {
            this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.0f, 0.3f, 0, maxOuterDotsRadius * 0.8f);
        } else {
            this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.3f, 1f, 0.8f * maxOuterDotsRadius, maxOuterDotsRadius);
        }
        if (currentProgress == 0) {
            this.currentDotSize1 = 0;
        } else if (currentProgress < 0.7) {
            this.currentDotSize1 = maxDotSize;
        } else {
            this.currentDotSize1 = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.7f, 1f, maxDotSize, 0);
        }
    }

    private void updateDotsPaints() {
        if (currentProgress < 0.5f) {
            float progress = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0f, 0.5f, 0, 1f);
            circlePaints[0].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)));
            circlePaints[1].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)));
            circlePaints[2].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)));
            circlePaints[3].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)));
        } else {
            float progress = (float) Utils.mapValueFromRangeToRange(currentProgress,
                    0.5f, 1f, 0, 1f);
            circlePaints[0].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)));
            circlePaints[1].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)));
            circlePaints[2].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)));
            circlePaints[3].setColor(new Color((int) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)));
        }
    }

    /**
     * Set color
     *
     * @param primaryColor   primaryColor
     * @param secondaryColor secondaryColor
     */
    public void setColors(int primaryColor, int secondaryColor) {
        COLOR_1 = primaryColor;
        COLOR_2 = secondaryColor;
        COLOR_3 = primaryColor;
        COLOR_4 = secondaryColor;
        invalidate();
    }

    private void updateDotsAlpha() {
        float progress = (float) Utils.clamp(currentProgress, 0.6f, 1f);
        int alpha = (int) Utils.mapValueFromRangeToRange(progress, 0.6f, 1f, 255, 0);
        circlePaints[0].setAlpha(Utils.getAlphaFromInt(alpha));
        circlePaints[1].setAlpha(Utils.getAlphaFromInt(alpha));
        circlePaints[2].setAlpha(Utils.getAlphaFromInt(alpha));
        circlePaints[3].setAlpha(Utils.getAlphaFromInt(alpha));
    }

    /**
     * Set the size
     *
     * @param width  width
     * @param height height
     */
    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;
        invalidate();
    }
}